package com.tool;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.swing.text.StyledEditorKit.BoldAction;

import org.junit.Test;

import com.mysql.jdbc.Statement;
import com.sz.pojo.User;

/**
 * 使用方法：先调用构造方法，创建一个实例，之后在调用其他方法。
 * 
 * 常用方法：query();
 * 
 * @author Administrator
 *
 */
public class Page<T> {
	private final static Integer PAGESIZE = 10; // 默认每页的最大记录数为10
	private static final Integer PAGENUM = 1; // 默认是第一页
	/**
	 * 页大小：每页的最大记录数，默认是每页有十条记录。
	 */
	private Integer pageSize = PAGESIZE;
	/**
	 * 当前页：当前页是第几页，默认是第一页。
	 */
	private Integer pageNum = PAGENUM;
	/**
	 * 页总数：查询到的所有记录的总数。
	 */
	private Integer total = 0;
	/**
	 * 总页数：查询到的所有记录经过计算后得到的总页数。
	 */
	private Integer totalPages = 0;
	/**
	 * 开始标记：标记开始查询的记录数，如果有数据，则从1开始，表示第一条数据。
	 */
	private Integer startItems = 0;
	/**
	 * 结束标记： 标记查询结束的记录数
	 */
	private Integer endItems = 0;
	/**
	 * 缓存列表：保存查询到的数据，和用于给用户使用的list属性不同，该属性只是用于保存。
	 */
	private List<Object> eachList = new ArrayList<Object>();
	/**
	 * 展示列表：用于保存给用户使用的数据
	 */
	private List<Object> list = new ArrayList<Object>();
	/**
	 * 初始化数据类型：初始化时的参数是String类型的sql语句
	 */
	private Boolean isStringSql = false;

	/**
	 * 初始化数据类型：初始化时的参数是ResultSet类型的结果集
	 */
	private Boolean isResultSet = false;

	/**
	 * 初始化数据类型：初始化时的参数是pojo对象集合类型
	 */
	private Boolean isList = false;
	/**
	 * sql语句：使用sql语句的形式查询数据，则保存sql语句到该属性
	 */
	private String oldSql = "";
	/**
	 * 用于保存pojo对象
	 */
	private T object = null;

	// ************** 构造方法 ******************************************

	/**
	 * pojo对象的集合列表
	 * 
	 * @param obj pojo对象的集合列表
	 */
	public Page(List<Object> obj) {
		super();
		// 把pojo对象赋值给缓存列表
		this.eachList = obj;
		// 设置总记录的数目
		this.total = obj.size();
		// 设置初始化数据类型
		this.isList = true;
		// 更新总页数
		this.computeTotalPages();
		if (obj.size() > 1) {
			// 给 展示列表list 赋值
			this.toList(obj);
		} else {
			System.out.println("pojo对象的集合列表为空");
		}
	}

	/**
	 * ResultSet类型集合
	 * 
	 * @param rs  查询数据库的到ResultSet集合
	 * @param obj pojo对象的实例，既是Dao层次的。
	 */
	public Page(T obj, ResultSet rs) {
		super();
		this.object = obj;
		// 给 展示列表list 赋值
		this.toList(rs, obj);
	}

	/**
	 * String：sql查询语句
	 * 
	 * @param obj pojo对象的实例，既是Dao层次的。
	 * @param str sql语句（查询语句：select * from user;）
	 */
	public Page(T obj, String str) {
		super();
		this.oldSql = str;
		this.object = obj;
		// 设置初始化数据类型
		this.isStringSql = true;
		this.toList(str, obj);
	}

	// ************** 计算方法 ************************************************
	/**
	 * 查询上一页的数据。
	 * 
	 * @param <K>
	 * @param pojo pojo对象的实例
	 * @return
	 */
	public <K> List<Object> previous() {
		if (this.startItems > this.pageSize) {
			this.list = new ArrayList<Object>();
			this.list = this.query(this.startItems - this.pageSize, this.startItems - 1);
		} else {
			this.list = new ArrayList<Object>();
			this.list = this.query(1, this.pageSize);
		}
		return this.list;
	}

	/**
	 * 查询下一页的数据。
	 * 
	 * @param <K>
	 * @param pojo pojo对象的实例
	 * @return
	 */
	public <K> List<Object> next() {
		if (this.endItems < this.total) {
			this.list = new ArrayList<Object>();
			this.list = this.query(this.endItems + 1, this.endItems + this.pageSize);
		} else {
			return this.list;
		}
		return this.list;
	}

	/**
	 * 单独查询第 pagenum_ 页的数据
	 * @param pagenum_ 查询的页数
	 * @return 返回展示列表list
	 */
	public List<Object> queryPage(Integer pagenum_) {
		if (pagenum_ <= this.totalPages && pagenum_ > 0) {
			this.list = new ArrayList<Object>();
			Integer startIt = this.pageSize * (pagenum_ - 1) + 1;
			Integer endIt = startIt + this.pageSize - 1;
			this.query(startIt, endIt);
		} else if (pagenum_ <= 0) {
			this.query(1, this.pageSize);
		} else if (pagenum_ > this.totalPages) {
			this.query(this.total - this.pageSize + 1, this.total);
		} else {
			System.out.println("发生未知错误！！-");
			return null;
		}
		return this.list;
	}

	/**
	 * 查询第 startItems_ 条记录，到第 endItems_ 条记录。
	 * 
	 * @param startItems_ 开始标记（包括）
	 * @param endItems_   结束标记（包括）
	 * @param pojo        pojo对象的实例
	 * @return 返回展示列表list，错误返回Null
	 */
	public <K> List<Object> query(Integer startItems_, Integer endItems_) {
		// 先预处理startItems_、endItems_
		if (startItems_ > 0 && startItems_ < this.total) {
			this.list = new ArrayList<Object>();
			if (this.isList || this.isResultSet) {
				// System.out.println("isList、isResultSet");
				// 更新标记
				this.startItems = startItems_;
				if (this.eachList.size() >= endItems_) {
					for (int i = startItems_ - 1; i < endItems_; i++) {
						this.list.add(this.eachList.get(i));
					}
					this.endItems = endItems_;
				} else {
					for (int i = startItems_ - 1; i < this.eachList.size(); i++) {
						this.list.add(this.eachList.get(i));
					}
					this.endItems = this.total;
					System.out.println("最大记录数超过数据库存储总数，已查询全部");
				}
				// 更新当前页
				computeCurrentPagesNum();
			} else if (this.isStringSql) {
				// System.out.println("isStringSql");
				// 更新标记
				this.startItems = startItems_;
				K poj = (K) this.object;
				if (this.total > endItems_) {
					Integer count = endItems_ - startItems_ + 1;
					String newSqlLimit = oldSql + " limit " + (startItems_ - 1) + "," + count;
					this.list = MySQL.queryPojoObjectList(poj, newSqlLimit);
					this.endItems = endItems_;
				} else {
					Integer count = this.total - startItems_ + 1;
					String newSqlLimit = oldSql + " limit " + (startItems_ - 1) + "," + count;
					this.list = MySQL.queryPojoObjectList(poj, newSqlLimit);
					this.endItems = this.total;
					System.out.println("最大记录数超过数据库存储总数，已查询全部");
				}
				// 更新当前页
				computeCurrentPagesNum();
			} else {
				System.out.println("发生未知错误！！");
				return null;
			}
		} else {
			System.out.println("开始标记不能小于1！且开始标记不能大于总记录数！！");
			return null;
		}
		return this.list;
	}

	/**
	 * 给属性list赋值,
	 * 
	 * 如果参数是ArrayList类型：截取缓存列表eachList的部分记录（更具也大小来截取）
	 * 
	 * 如果参数是ResultSet类型：
	 * 
	 * @param obj ArrayList类型或ResultSet类型
	 */
	private <K> void toList(Object obj, K... pojo) {
		if (obj.getClass().equals(ArrayList.class)) {
			// 先清空属性list的值
			this.list = new ArrayList<Object>();
			if (this.pageSize < this.eachList.size()) {
				for (int i = 0; i < this.pageSize; i++) {
					this.list.add(this.eachList.get(i));
				}
			} else {// 意味着缓存列表eachList的长度小于每页的长度
				for (int i = 0; i < this.eachList.size(); i++) {
					this.list.add(this.eachList.get(i));
				}
			}
			// 调整记录开始和结束的标记
			this.startItems = 1;
			this.endItems = this.list.size();
		} else if (ResultSet.class.isInstance(obj)) {
			ResultSet rs = (ResultSet) obj;
			if (pojo.length > 0) {
				try {
					rs.last(); // 光标移到最后一行
					int count = rs.getRow();// 判断行的总数
					rs.beforeFirst();// 返回第一行之前
					this.total = count;// 设置总记录的数目
					this.isResultSet = true;// 设置初始化数据类型
					this.computeTotalPages();// 更新总页数
					List<T> lis = (List<T>) queryPojoObjectList(pojo[0], rs);
					if (lis != null) {
						this.eachList = (List<Object>) lis;// 给 缓存列表eachList 赋值
						this.list = new ArrayList<Object>();// 先清空属性list的值
						if (this.pageSize < this.eachList.size()) {
							for (int i = 0; i < this.pageSize; i++) {
								this.list.add(this.eachList.get(i));
							}
						} else {// 意味着缓存列表eachList的长度小于每页的长度
							for (int i = 0; i < this.eachList.size(); i++) {
								this.list.add(this.eachList.get(i));
							}
						}
					} else {
						System.out.println("pojo对象的集合列表为空");
					}
					// 调整记录开始和结束的标记
					this.startItems = 1;
					this.endItems = this.list.size();
				} catch (SQLException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println("没有传入pojo对象******");
			}
		} else if (obj.getClass().equals(String.class)) {
			String sql = (String) obj;
			T poj = (T) pojo[0];
			// 调整记录开始和结束的标记
			this.startItems = 1;
			this.endItems = this.pageSize;
			// 获取查询语句能查询到的所有数据总数
			String newsql = sql.replace("*", "count(*)");
			ResultSet rs = MySQL.query(newsql);
			if (rs != null) {
				try {
					while (rs.next()) {
						// System.out.println(rs.getObject(1).toString());
						this.total = Integer.parseInt(rs.getObject(1).toString());
					}
				} catch (SQLException e) {
					e.printStackTrace();
				}
			} else {
				System.out.println("查询所有数据总数失败");
			}
			// 更新总页数
			this.computeTotalPages();
			// 给 展示列表list 赋值
			if (this.total <= this.pageSize) {
				this.list = MySQL.queryPojoObjectList(poj, sql);
			} else {
				String newSqlLimit = sql + " limit " + 0 + "," + this.pageSize;
				this.list = MySQL.queryPojoObjectList(poj, newSqlLimit);
			}
		} else {
			System.out.println("发生未知错误！！--------------------");
		}

	}

	/**
	 * 查询到的数据自动给pojo列表对象序列化。
	 * 
	 * @param obj       pojo对象实例
	 * @param resultSet ResultSet类型的结果集
	 * @return 返回序列化后的pojo对象列表，失败返回Null或者空的pojo对象。
	 */
	public static <T> List<T> queryPojoObjectList(T obj, ResultSet resultSet) {
		List<T> list = new ArrayList<T>();
		ResultSet rs = resultSet;// 查询数据并返回记过集
		Class rsC = ResultSet.class;// 获取ResultSet.class，下面要用到
		if (rs != null) {// 有数据
			try {
				Class cla = Class.forName(obj.getClass().getTypeName());// 创建传入的obj对象的Class类对象
				Field[] f = cla.getDeclaredFields();// 获取传入的obj对象的属性数组
				rs.last();// 光标移到最后一行
				int count = rs.getRow();// 判断行的总数
				rs.beforeFirst();// 返回第一行之前
				// System.out.println(count);
				if (count == 0) {
					System.out.println("没有查询到想要的数据...");
					return null;
				} else if (count >= 1) {
					while (rs.next()) {
						Object newobj = cla.newInstance();
						for (Field ff : f) {
							// 判断传入obj对象的属性的数据类型，执行相应的代码块。
							if (ff.getType().equals(int.class) || ff.getType().equals(Integer.class)) {
								// 因为Results对象没有getInteger（），所以要判断一次
								int date = rs.getInt(ff.getName());
								String methodName = "set" + MySQL.upperCase(ff.getName());
								if (ff.getType().equals(Integer.class)) {
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, Integer.class);
									m.invoke(newobj, date);
								} else {
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, int.class);
									m.invoke(newobj, date);
								}
							} else {
								// 因为大部分的数据类型都是有完整的包名的（除了int），所以用split方法截取类型字符串。
								String[] s = ff.getType().getName().split("\\.");
								if (s.length >= 0) {
									String str = s[s.length - 1];
									// 获取ResultSet对象实例的方法
									Method rsM = rsC.getDeclaredMethod("get" + str, ff.getType());
									// 获取传入obj对象的属性的set方法名
									String methodName = "set" + MySQL.upperCase(ff.getName());
									// 获取传入obj对象的方法
									@SuppressWarnings("unchecked")
									Method m = cla.getDeclaredMethod(methodName, ff.getType());
									// 执行传入obj对象的方法和ResultSet对象实例的方法
									m.invoke(newobj, rsM.invoke(rs, ff.getName()));
								} else {
									System.out.println("split方法截取类型字符串长度为0，需要重新设计方法。");
								}
							}
						}
						list.add((T) newobj);
					}
				}
			} catch (SecurityException | ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SQLException e) {
				e.printStackTrace();
			} catch (NoSuchMethodException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("没有查询到数据。");
			return null;
		}

		return list;
	}

	/**
	 * 更新当前页：计算当前页是第几页。。。
	 */
	private void computeCurrentPagesNum() {
		Integer count = this.endItems / this.pageSize;
		if (count == 0) {
			count++;
		} else if (this.endItems % this.pageSize != 0) {
			count++;
		}
		this.pageNum = count;
	}

	/**
	 * 计算查询到的数据能够分成多少页，该方法是计算页数。
	 */
	private void computeTotalPages() {
		if (this.total != 0) {
			Integer t = this.total;
			Integer ps = this.pageSize;
			Integer count = t / ps;
			if (t % ps > 0) {
				count++;
			}
			this.totalPages = count;
		}

	}

	// ************** set 和 get 方法
	// **************************************************************
	/**
	 * 每页的最大记录数，默认是每页有十条记录。
	 */
	public Integer getPageSize() {
		return pageSize;
	}

	/**
	 * 每页的最大记录数，默认是每页有十条记录。
	 */
	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
		this.query(this.startItems, this.startItems + this.pageSize - 1);
	}

	/**
	 * 当前页是第几页，默认是第一页。
	 */
	public Integer getPageNum() {
		return pageNum;
	}

	/**
	 * 当前页是第几页，默认是第一页。
	 */
	public void setPageNum(Integer pageNum) {
		this.pageNum = pageNum;
	}

	/**
	 * 查询到的所有记录的总数。
	 */
	public Integer getTotal() {
		return total;
	}

	/**
	 * 查询到的所有记录的总数。
	 */
	public void setTotal(Integer total) {
		this.total = total;
	}

	/**
	 * 查询到的所有记录进过计算后得到的总页数。
	 */
	public Integer getTotalPages() {
		return totalPages;
	}

	/**
	 * 查询到的所有记录进过计算后得到的总页数。
	 */
	public void setTotalPages(Integer totalPages) {
		this.totalPages = totalPages;

	}

	/**
	 * 标记开始查询的记录数，如果有数据，则从1开始，表示第一条数据。
	 */
	public Integer getStartItems() {
		return startItems;
	}

	/**
	 * 标记查询结束的记录数
	 */
	public Integer getEndItems() {
		return endItems;
	}

	/**
	 * 用于保存给用户使用的数据
	 */
	public List<Object> getList() {
		return list;
	}

	/**
	 * 用于保存给用户使用的数据
	 */
	public void setList(List<Object> list) {
		this.list = list;
	}
}
